package com.yycx.bpm.provider.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.yycx.bpm.client.entity.FlowProcess;
import com.yycx.bpm.provider.common.CommentTypeEnum;
import com.yycx.bpm.provider.common.ResponseFactory;
import com.yycx.bpm.provider.common.cmd.AddCcIdentityLinkCmd;
import com.yycx.bpm.provider.common.handler.ProcInstDeleteHandler;
import com.yycx.bpm.provider.common.handler.ProcessStartHandler;
import com.yycx.bpm.provider.constant.FlowableConstant;
import com.yycx.bpm.provider.model.CategoryVo;
import com.yycx.bpm.provider.model.ProcessInstanceQueryVo;
import com.yycx.bpm.provider.model.ProcessInstanceRequest;
import com.yycx.bpm.provider.service.FlowProcessService;
import com.yycx.bpm.provider.service.ProcessDefinitionService;
import com.yycx.bpm.provider.service.ProcessInstanceService;
import com.yycx.common.mybatis.model.ResultBody;
import com.yycx.common.security.OpenHelper;
import com.yycx.common.security.OpenUser;
import com.yycx.common.utils.ApiAssert;
import com.yycx.common.base.utils.FlymeUtils;
import com.yycx.common.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.flowable.common.engine.impl.identity.Authentication;
import org.flowable.engine.HistoryService;
import org.flowable.engine.ManagementService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.impl.persistence.entity.ExecutionEntity;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 流程实例
 *
 * @author zyf
 */
@Slf4j
@Service
@Transactional(rollbackFor = Exception.class)
public class ProcessInstanceServiceImpl implements ProcessInstanceService {
    @Resource
    protected ResponseFactory responseFactory;
    @Resource
    protected ManagementService managementService;
    @Resource
    protected RuntimeService runtimeService;
    @Resource
    protected HistoryService historyService;
    @Resource
    protected PermissionServiceImpl permissionService;
    @Resource
    protected FlowableTaskServiceImpl flowableTaskService;
    @Resource
    protected TaskService taskService;
    @Resource
    private FlowProcessService flowProcessService;

    @Resource
    private ProcessDefinitionService processDefinitionService;

    @Autowired(required = false)
    private Map<String, ProcInstDeleteHandler> procInstDeleteHandlerMap;

   /* @Resource
    private FlowableCommonMapper flowableCommonMapper;*/

    @Override
    public ProcessInstance getProcessInstanceById(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        ApiAssert.isNotEmpty("流程实例未定义", processInstance);
        return processInstance;
    }


    @Override
    public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) {
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery()
                .processInstanceId(processInstanceId).singleResult();
        ApiAssert.isNotEmpty("流程实例未定义", historicProcessInstance);
        return historicProcessInstance;
    }

    /**
     * 启动流程
     *
     * @param processInstanceRequest
     */
    @Override
    public void start(ProcessInstanceRequest processInstanceRequest) {
        String processDefinitionId = FlymeUtils.getString(processInstanceRequest.getProcessDefinitionId());
        String processDefinitionKey = FlymeUtils.getString(processInstanceRequest.getProcessDefinitionKey());
        String handlerName = FlymeUtils.getString(processInstanceRequest.getHandlerName());
        String businessKey = processInstanceRequest.getBusinessKey();
        OpenUser user = OpenHelper.getUser();
        String userId = user.getUserId().toString();
        if (FlymeUtils.isNotEmpty(businessKey)) {
            String[] keyIds = businessKey.split(",");
            for (String keyId : keyIds) {
                processInstanceRequest.setBusinessKey(keyId);
                //流程启动业务拦截器
                ProcessStartHandler processStartHandler = SpringContextHolder.getHandler(handlerName, ProcessStartHandler.class);
                if (FlymeUtils.isNotEmpty(processStartHandler)) {
                    businessKey = processStartHandler.init(processInstanceRequest);
                    String processKey = processInstanceRequest.getProcessKey();
                    if (FlymeUtils.isNotEmpty(processKey)) {
                        FlowProcess flowProcess = flowProcessService.getByProcessKey(processKey);
                        if (FlymeUtils.isNotEmpty(flowProcess)) {
                            processInstanceRequest.setProcessDefinitionId(flowProcess.getProcDefId());
                            processDefinitionId = flowProcess.getProcDefId();
                            ProcessDefinition processDefinition = processDefinitionService.getProcessDefinitionById(flowProcess.getProcDefId());
                            if (FlymeUtils.isNotEmpty(processDefinition)) {
                                processInstanceRequest.setProcessDefinitionKey(processDefinition.getKey());
                                processDefinitionKey = processDefinition.getKey();
                            }
                        }
                    }

                }
                ApiAssert.anyOneIsNotNull("流程未定义:" + processDefinitionKey, processDefinitionId, processDefinitionKey);

                //查询流程定义
                ProcessDefinition definition = permissionService.validateReadPermissionOnProcessDefinition(userId, processDefinitionId, processDefinitionKey, processInstanceRequest.getTenantId());
                Map<String, Object> formMap = processInstanceRequest.getFormMap();
                Map<String, Object> variables = formMap;

                //设置流程发起人
                Authentication.setAuthenticatedUserId(userId);
                //构建流程实例
                ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder();
                processInstanceBuilder.processDefinitionId(definition.getId());

                // 流程实例标题
                processInstanceBuilder.name(definition.getName() + "-" + user.getUsername());

                if (FlymeUtils.isNotEmpty(processStartHandler)) {
                    if (FlymeUtils.isNotEmpty(processStartHandler)) {
                        //初始化流程变量
                        variables = processStartHandler.initVariables(processInstanceRequest, definition);
                    }
                    if (FlymeUtils.isEmpty(variables)) {
                        variables = new HashMap<>();
                    }
                    variables.put("businessKey", businessKey);
                }
                // 业务key
                processInstanceBuilder.businessKey(businessKey);
                //租户ID使用企业ID
                processInstanceBuilder.tenantId(user.getCompanyId().toString());
                processInstanceBuilder.variables(variables);
                ProcessInstance instance = processInstanceBuilder.start();
                String processInstanceId = instance.getProcessInstanceId();
                List<Task> tasks = taskService.createTaskQuery().processInstanceId(processInstanceId).list();

                //启动完成业务处理
                if (FlymeUtils.isNotEmpty(processStartHandler)) {
                    processStartHandler.afterComplete(processInstanceRequest, definition, instance, tasks, variables);
                }
                for (Task task : tasks) {
                    // 约定：发起者节点为 __initiator__ ,则自动完成任务
                    if (FlowableConstant.INITIATOR.equals(task.getTaskDefinitionKey())) {
                        flowableTaskService.addComment(task.getId(), processInstanceId, userId, CommentTypeEnum.TJ, null);
                        if (FlymeUtils.isEmpty(task.getAssignee())) {
                            taskService.setAssignee(task.getId(), userId);
                        }
                        taskService.complete(task.getId());
                        if (FlymeUtils.isNotEmpty(processInstanceRequest.getCcToVos())) {
                            managementService.executeCommand(new AddCcIdentityLinkCmd(processInstanceId, task.getId(), userId, processInstanceRequest.getCcToVos()));
                        }
                    }
                }
            }
        }

    }

    /**
     * 删除流程实例
     *
     * @param processInstanceId
     * @param cascade
     * @param deleteReason
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String processInstanceId, boolean cascade, String deleteReason) {
        HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(processInstanceId);
        String businessKey = historicProcessInstance.getBusinessKey();
        String processDefinitionKey = historicProcessInstance.getProcessDefinitionKey();
        ResultBody prepare = procInstDeletePrepare(historicProcessInstance);
        if (!prepare.isOk()) {
            ApiAssert.failure(prepare.getMessage());
        }
        if (historicProcessInstance.getEndTime() != null) {
            historyService.deleteHistoricProcessInstance(historicProcessInstance.getId());
            return;
        }
        ExecutionEntity executionEntity = (ExecutionEntity) getProcessInstanceById(processInstanceId);
        if (FlymeUtils.isNotEmptyAfterTrim(executionEntity.getSuperExecutionId())) {
            ApiAssert.failure("子流程禁止删除");
        }
        runtimeService.deleteProcessInstance(processInstanceId, deleteReason);
        if (cascade) {
            historyService.deleteHistoricProcessInstance(processInstanceId);
        }
        ResultBody success = procInstDeleteSuccess(processDefinitionKey, businessKey, processInstanceId);
        if (!success.isOk()) {
            ApiAssert.failure(prepare.getMessage());
        }
    }

    /**
     * 激活流程实例
     *
     * @param processInstanceId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void activate(String processInstanceId) {
        ProcessInstance processInstance = getProcessInstanceById(processInstanceId);
        if (!processInstance.isSuspended()) {
            ApiAssert.failure("流程实例未挂起，id为" + processInstanceId);
        }
        runtimeService.activateProcessInstanceById(processInstance.getId());
    }

    /**
     * 挂起流程实例
     *
     * @param processInstanceId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void suspend(String processInstanceId) {
        ProcessInstance processInstance = getProcessInstanceById(processInstanceId);
        if (processInstance.isSuspended()) {
            ApiAssert.failure("流程实例已挂起，id为" + processInstanceId);
        }
        runtimeService.suspendProcessInstanceById(processInstance.getId());
    }

    @Override
    public List listMyInvolvedSummary(ProcessInstanceQueryVo processInstanceQueryVo, String userId) {
        processInstanceQueryVo.setUserId(userId);
        List<CategoryVo> result = new ArrayList<>();
      /*  List<ProcessDefinitionVo> vos = flowableCommonMapper.listMyInvolvedSummary(listMyInvolvedSummaryVo);

        Map<String, List<ProcessDefinitionVo>> categorysByParent = new HashMap<>();
        for (ProcessDefinitionVo vo : vos) {
            List<ProcessDefinitionVo> childs = categorysByParent.computeIfAbsent(vo.getCategory(), k -> new ArrayList<>());
            childs.add(vo);
        }
        for (Map.Entry<String, List<ProcessDefinitionVo>> entry : categorysByParent.entrySet()){
            CategoryVo aCategoryVo = new CategoryVo();
            aCategoryVo.setCategory(entry.getKey());
            aCategoryVo.setProcessDefinitionVoList(entry.getValue());
            String categoryName = entry.getValue().iterator().next().getCategoryName();
            aCategoryVo.setCategoryName(categoryName);
            result.add(aCategoryVo);
        }*/
        return result;
    }

    /**
     * 流程实例删除
     *
     * @param historicProcessInstance
     */
    private ResultBody procInstDeletePrepare(HistoricProcessInstance historicProcessInstance) {
        if (ObjectUtil.isNotEmpty(historicProcessInstance)) {
            String processDefinitionKey = historicProcessInstance.getProcessDefinitionKey();
            for (ProcInstDeleteHandler procInstDeleteHandler : procInstDeleteHandlerMap.values()) {
                if (procInstDeleteHandler.support(processDefinitionKey)) {
                    return procInstDeleteHandler.prepare(historicProcessInstance);
                }
            }
        }
        return ResultBody.ok();

    }

    /**
     * 流程实例删除
     *
     * @param processDefinitionKey
     * @param businessKey
     * @param processInstanceId
     */
    private ResultBody procInstDeleteSuccess(String processDefinitionKey, String businessKey, String processInstanceId) {
        if (ObjectUtil.isNotEmpty(processDefinitionKey)) {
            for (ProcInstDeleteHandler procInstDeleteHandler : procInstDeleteHandlerMap.values()) {
                if (procInstDeleteHandler.support(processDefinitionKey)) {
                    return procInstDeleteHandler.success(businessKey, processInstanceId);
                }
            }
        }
        return ResultBody.ok();

    }
}
